<?php

namespace handle\message\lib\manage\strategy;

use Closure;
use handle\message\lib\traits\ManageSingleton;

class ChessBoard
{
    use ManageSingleton;

    /**
     * 判断落子后的四条线是否有五子连珠
     * @param array $board 整局棋盘
     * @param array $coordinate 落子坐标
     * @param int $chessColor 落子颜色
     * @return bool
     */
    public function checkGameOver(array $board, array $coordinate, int $chessColor): bool
    {
        $checkHorizontal = $this->createIsWin(
            function($x, $y){
                return [$x, $y - 1];
            },
            function($x, $y){
                return [$x, $y + 1];
            },
        );
        $checkVertical = $this->createIsWin(
            function($x, $y){
                return [$x - 1, $y];
            },
            function($x, $y){
                return [$x + 1, $y];
            },
        );
        $checkDiagonal = $this->createIsWin(
            function($x, $y){
                return [$x - 1, $y - 1];
            },
            function($x, $y){
                return [$x + 1, $y + 1];
            },
        );
        $checkReDiagonal = $this->createIsWin(
            function($x, $y){
                return [$x - 1, $y + 1];
            },
            function($x, $y){
                return [$x + 1, $y - 1];
            },
        );
        return $checkHorizontal($board, $coordinate, $chessColor) || $checkVertical($board, $coordinate, $chessColor) || $checkDiagonal($board, $coordinate, $chessColor) || $checkReDiagonal($board, $coordinate, $chessColor);
    }

    /**
     * 验证这个落子的坐标是否有效
     * @param array $board 整局棋盘
     * @param array $coordinate 落子坐标
     * @param int $chessColor 落子颜色
     * @return bool
     */
    private function coordinateIsValid(array $board, array $coordinate, int $chessColor): bool
    {
        [$x, $y] = $coordinate;
        return $x >= 0 && $x < count($board) && $y >= 0 && $y < count($board[$x]) && $board[$x][$y] === $chessColor;
    }

    /**
     * 判读落子后的某条线上是否有五子连珠
     * @param Closure $p1Move   落子后的左边坐标
     * @param Closure $p2Move   落子后的右边坐标
     * @return Closure
     */
    private function createIsWin(Closure $p1Move, Closure $p2Move): Closure
    {
        return function (array $board, array $coordinate, int $chessColor) use ($p1Move, $p2Move){
            $p1 = $p1Move($coordinate[0], $coordinate[1]);
            $p2 = $p2Move($coordinate[0], $coordinate[1]);
            $count = 1;
            while(true){
                $p1Flag = false;
                if($this->coordinateIsValid($board, $p1, $chessColor)){ //验证坐标是否有效
                    $count++;
                    $p1Flag = true;
                    $p1 = $p1Move($p1[0], $p1[1]);
                }
                $p2Flag = false;
                if($this->coordinateIsValid($board, $p2, $chessColor)){ //验证坐标是否有效
                    $count++;
                    $p2Flag = true;
                    $p2 = $p2Move($p2[0], $p2[1]);
                }
                if(!$p1Flag and !$p2Flag){  //如果左右两边的坐标都无效，则返回false
                    return false;
                }
                if($count >= 5){ //如果已经连续五个坐标有效了，则返回true
                    return true;
                }
            }
        };
    }
}

/***
 * $chessBoard = new ChessBoard();
 *
 * 水平线棋盘
 * $board = [
 * [0, 0, 0, 0, 0, 0],
 * [0, 1, 0, 1, 1, 1],
 * [0, 0, 0, 0, 0, 0],
 * [0, 0, 0, 0, 0, 0],
 * [0, 0, 0, 0, 0, 0],
 * ];
 *
 *
 *
 * 竖线棋盘
 * $board = [
 * [0, 0, 1, 0, 0, 0],
 * [0, 0, 0, 0, 0, 0],
 * [0, 0, 1, 0, 0, 0],
 * [0, 0, 1, 0, 0, 0],
 * [0, 0, 1, 0, 0, 0],
 * ];
 *
 *
 * 斜线棋盘
 * $board = [
 * [0, 1, 0, 0, 0, 0],
 * [0, 0, 5, 0, 0, 0],
 * [0, 0, 0, 1, 0, 0],
 * [0, 0, 0, 0, 1, 0],
 * [0, 0, 0, 0, 0, 1],
 * ];
 * $res = $chessBoard->checkGameOver($board, [1,2], 1);
 * var_dump($res);
 */